Sprite Byte: Manual Cycling and Sound

Level: Beginner - Intermediate

by Alyce Watson [http://alycesrestaurant.com/]

Home

SpriteByte

Handling Data

Data Six Pak

Kaliedoscope

Drag and Drop

Simulations w/LB

Design Template

Demos:

Simulated Hyperlink

Slideshow

Space Travel

Corrections:

TransparentBlt Fix

Video Capture Fix


Newsletter help

Index

Cycling the sprite image.

[See issue #128 for a discussion of changing the sprite image.]

Liberty BASIC has a special command that allows you to cycle through the frames of action of a sprite. This is great when you want to make a sprite look alive. A sprite man can appear to be walking, a fire can appear to be flaming, etc. The command is CYCLESPRITE

You can also use the SPRITEIMAGE command to cycle the sprite through its frames of animation in your code.

Why change the image yourself?

You can change the image yourself, by why would you do this? You might do it because you need more precise control over a sprite. What if your sprite man is walking part of the time, and crawling part of the time? You might have three different images for walking and three others for crawling. If you let Liberty BASIC cycle for you, the man would walk, then crawl, then walk, then crawl. Oops!

You might also need to synchronize other actions with the sprite animation, so knowing which frame (image) is being displayed can be critical. What if your sprite makes a clomping sound as he walks along? You'd want the sound to happen each time his foot hit the ground, of course!

What if you want to change the speed at which a sprite cycles in relation to the cycling of other sprites? You might have a bug that moves its legs quickly, and another that is a slow poke.

Taking control of the frames of animation.

If you want to switch the image yourself, it is easiest to have a variable that holds the index of the current image. If you give the images the right kind of names when you load them, you can cycle quite easily. Notice the loadbmp names of the sprite images:

    loadbmp "club1","club1.bmp"
    loadbmp "club2","club2.bmp"
    loadbmp "club3","club3.bmp"

    #main.g "addsprite guy club1 club2 club3"

You can access any of the three images by using a variable, like this:

    frame=2
    #main.g "spriteimage guy club";frame

The variable is added to the name "club." This is called string concatenation. In the example above, LB sees the command in this way:

    #main.g "spriteimage guy club2"

Since the sprite name is actually a string, you can also have an array that holds the names. Here's an example.

    guy$(1)="club1"
    guy$(2)="club2"
    guy$(3)="club3"
    
    #main.g "spriteimage guy ";guy$(2)

Liberty BASIC sees the command like this:

    #main.g "spriteimage guy club2"

Don't neglect the blank space. If you forget it like this:

    #main.g "spriteimage guy";guy$(2)

Then Liberty BASIC sees this incomprehensible command:

    #main.g "spriteimage guyclub2"

How do we make this animation work?

The easiest way to do this is by keeping a counter variable and incrementing it by "1" each time the new frame is displayed with drawsprites. If we call the counter variable frame, we'd change it like this:

    frame=frame+1   'advance frame of animation

Can you see how we'd use that with the methods above, either concatenating it with the sprite name, or using it as the index for the sprite array? In the demo, it looks like this:

    #main.g "spriteimage guy club";frame

Don't forget to check the value of the counter, and set it back to "1" if it goes over the limit. We have three images, so if the counter has a value greater than 3, we set it back to 1.

    if frame>3 then frame=1

Here is part of the main animation loop from the demo. Don't forget to use a SCAN command so that user events can be handled. Using the Sleep API call creates a pause so that the frame of animation stays on the screen for a brief time, and it frees the processor. [See issue #105]

[loop]
    scan
    frame=frame+1   'advance frame of animation
    if frame>3 then frame=1

    #main.g "spriteimage guy club";frame
    #main.g "drawsprites"
    calldll #kernel32, "Sleep", 200 as long, re as void
    goto [loop]

Synchronizing Sounds

The demo for this article has our little caveman with the club. We first met him in the last article. This time there is a rock in front of him and he swings his club at it. Each time his club makes contact with the rock, there is a sound effect.

We need to synchronize the sound with the action. If we play a wave with the LOOP mode, the sound will play over and over, but it won't be in synchrony with the action. To synchronize sound and action we'll play a wave ASYNC. That sounds strange! If we want to synchronize the sound, wouldn't we play it SYNC? Nope!

Async vs Sync

Liberty BASIC gives us three ways to play a wave sound. From the helpfile:

PLAYWAVE "filename" [, mode ]

Description:

This plays a *.wav sound from a file on disk as specified in filename. If mode is specified, it must be one of the modes described below:

sync (or synch) - wait for the wave file to finish playing (the default)

async (or asynch) - don't wait for the wave file to finish playing

loop - play the wave file over and over (cancel with: playwave "")

Playing a wave with the SYNCH or SYNC mode causes action to halt while the wave is playing. Although this may be useful sometimes, we don't want our caveman to pause each time he strikes the rock with his club. We'll use the ASYNCH or ASYNC mode so that action can continue as the sound effect plays. The code looks like this:

    playwave "hit.wav",async

Since we don't want to play the wave in each frame of animation, we check the value of our frame variable, and only play the wave when the third image is being displayed.

    if frame=3 then playwave "hit.wav",async

Here is the entire animation loop, including the incrementation of the counter variable, the changing of the image with spriteimage , the scan command, the Sleep API call, and the wave playing along with the image of the caveman striking the rock with his club, and of course the drawsprites command that causes the display to be updated on the screen:

[loop]
    scan
    frame=frame+1   'advance frame of animation
    if frame>3 then frame=1

    #main.g "spriteimage guy club";frame
    #main.g "drawsprites"
    if frame=3 then playwave "hit.wav",async
    calldll #kernel32, "Sleep", 200 as long, re as void
    goto [loop]

The demo follows. We can accomplish quite a lot with very little code!


DEMO

Please download the zipped archive of this issue to get the bitmaps and wave file.

[WindowSetup]
    NOMAINWIN
    WindowWidth = 238 : WindowHeight = 200
    UpperLeftX = INT((DisplayWidth-WindowWidth)/2)
    UpperLeftY = INT((DisplayHeight-WindowHeight)/2)

[ControlSetup]
graphicbox  #main.g, 1, 1, 228, 180

Open "Cycling & Sound" for Window_nf as #main
    #main "trapclose [quit]"
    #main.g "down; fill brown; flush"
    #main.g "getbmp bkg 0 0 228 178"
    #main.g "background bkg"

[SpriteSetup]
    loadbmp "club1","club1.bmp"
    loadbmp "club2","club2.bmp"
    loadbmp "club3","club3.bmp"
    loadbmp "stone","stone.bmp"

    #main.g "addsprite stone stone"
    #main.g "spritexy stone 70 80"
    #main.g "addsprite guy club1 club2 club3"
    #main.g "spritexy guy 85 50"

[loop]
    scan
    frame=frame+1   'advance frame of animation
    if frame>3 then frame=1

    #main.g "spriteimage guy club";frame
    #main.g "drawsprites"
    if frame=3 then playwave "hit.wav",async
    calldll #kernel32, "Sleep", 200 as long, re as void
    goto [loop]

[quit]
    unloadbmp "club1"
    unloadbmp "club2"
    unloadbmp "club3"
    unloadbmp "stone"
    close #main : END


'Game artwork created by Ari Feldman ari@arifeldman.com


Home

SpriteByte

Handling Data

Data Six Pak

Kaliedoscope

Drag and Drop

Simulations w/LB

Design Template

Demos:

Simulated Hyperlink

Slideshow

Space Travel

Corrections:

TransparentBlt Fix

Video Capture Fix


Newsletter help

Index